
// P_inter.c

#include "DoomDef.h"
#include "map.h"
#include "p_spec.h"
#include "p_tick.h"
#include "p_inter.h"
#include "p_map.h"
#include "soundst.h"
#include "p_local.h"
#include "p_mobj.h"
#include "p_pspr.h"
#include "sb_bar.h"
#include "p_user.h"
#include "p_enemy.h"

#define BONUSADD 6

int WeaponValue[] =
{
	1,		// staff
	3,		// goldwand
	4,		// crossbow
	5,		// blaster
	6,		// skullrod
	7,		// phoenixrod
	8,		// mace
	2,		// gauntlets
	0		// beak
};

int maxammo[NUMAMMO] =
{
	100,	// gold wand
	50,		// crossbow
	200,	// blaster
	200,	// skull rod
	20,		// phoenix rod
	150		// mace
};

static int GetWeaponAmmo[NUMWEAPONS] =
{
	0,		// staff
	25,		// gold wand
	10,		// crossbow
	30,		// blaster
	50,		// skull rod
	2,		// phoenix rod
	50,		// mace
	0,		// gauntlets
	0		// beak
};

static weapontype_t GetAmmoChange[] =
{
	wp_goldwand,
	wp_crossbow,
	wp_blaster,
	wp_skullrod,
	wp_phoenixrod,
	wp_mace
};

/*
static boolean GetAmmoChangePL1[NUMWEAPONS][NUMAMMO] =
{
	// staff
	{wp_goldwand, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace},
	// gold wand
	{-1, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace},
	// crossbow
	{-1, -1, wp_blaster, wp_skullrod, -1, -1},
	// blaster
	{-1, -1, -1, -1, -1, -1},
	// skull rod
	{-1, -1, -1, -1, -1, -1},
	// phoenix rod
	{-1, -1, -1, -1, -1, -1},
	// mace
	{-1, wp_crossbow, wp_blaster, wp_skullrod, -1, -1},
	// gauntlets
	{-1, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace}
};
*/

/*
static boolean GetAmmoChangePL2[NUMWEAPONS][NUMAMMO] =
{
	// staff
	{wp_goldwand, wp_crossbow, wp_blaster, wp_skullrod, wp_phoenixrod,
		wp_mace},
	// gold wand
	{-1, wp_crossbow, wp_blaster, wp_skullrod, wp_phoenixrod, wp_mace},
	// crossbow
	{-1, -1, wp_blaster, wp_skullrod, wp_phoenixrod, -1},
	// blaster
	{-1, -1, -1, wp_skullrod, wp_phoenixrod, -1},
	// skull rod
	{-1, -1, -1, -1, -1, -1},
	// phoenix rod
	{-1, -1, -1, -1, -1, -1},
	// mace
	{-1, wp_crossbow, wp_blaster, wp_skullrod, -1, -1},
	// gauntlets
	{-1, -1, -1, wp_skullrod, wp_phoenixrod, wp_mace}
};
*/

//--------------------------------------------------------------------------
//
// PROC P_SetMessage
//
//--------------------------------------------------------------------------

boolean ultimatemsg;

void P_SetMessage(player_t *player, char *message, boolean ultmsg)
{
	extern boolean messageson;
	
	if((ultimatemsg || !messageson) && !ultmsg)
	{
		return;
	}
	player->message = message;
	player->messageTics = MESSAGETICS;
	BorderTopRefresh = true;
	if(ultmsg)
	{
		ultimatemsg = true;
	}
}

//--------------------------------------------------------------------------
//
// FUNC P_GiveAmmo
//
// Returns true if the player accepted the ammo, false if it was
// refused (player has maxammo[ammo]).
//
//--------------------------------------------------------------------------

boolean P_GiveAmmo(player_t *player, ammotype_t ammo, int count)
{
	int prevAmmo;
	//weapontype_t changeWeapon;

	if(ammo == am_noammo)
	{
		return(false);
	}
	if(ammo < 0 || ammo > NUMAMMO)
	{
		I_Error("P_GiveAmmo: bad type %i", ammo);
	}
	if(player->ammo[ammo] == player->maxammo[ammo])
	{
		return(false);
	}
	if(gameskill == sk_baby || gameskill == sk_nightmare)
	{ // extra ammo in baby mode and nightmare mode
		count += count>>1;
	}
	prevAmmo = player->ammo[ammo];

	player->ammo[ammo] += count;
	if(player->ammo[ammo] > player->maxammo[ammo])
	{
		player->ammo[ammo] = player->maxammo[ammo];
	}
	if(prevAmmo)
	{
		// Don't attempt to change weapons if the player already had
		// ammo of the type just given
		return(true);
	}
	if(player->readyweapon == wp_staff
		|| player->readyweapon == wp_gauntlets)
	{
		if(player->weaponowned[GetAmmoChange[ammo]])
		{
			player->pendingweapon = GetAmmoChange[ammo];
		}
	}
/*
	if(player->powers[pw_weaponlevel2])
	{
		changeWeapon = GetAmmoChangePL2[player->readyweapon][ammo];
	}
	else
	{
		changeWeapon = GetAmmoChangePL1[player->readyweapon][ammo];
	}
	if(changeWeapon != -1)
	{
		if(player->weaponowned[changeWeapon])
		{
			player->pendingweapon = changeWeapon;
		}
	}
*/
	return(true);
}

//--------------------------------------------------------------------------
//
// FUNC P_GiveWeapon
//
// Returns true if the weapon or its ammo was accepted.
//
//--------------------------------------------------------------------------

boolean P_GiveWeapon(player_t *player, weapontype_t weapon)
{
	boolean gaveAmmo;
	boolean gaveWeapon;

	if(netgame && !deathmatch)
	{ // Cooperative net-game
		if(player->weaponowned[weapon])
		{
			return(false);
		}
		player->bonuscount += BONUSADD;
		player->weaponowned[weapon] = true;
		P_GiveAmmo(player, wpnlev1info[weapon].ammo,
			GetWeaponAmmo[weapon]);
		player->pendingweapon = weapon;
		if(player == &players[consoleplayer])
		{
			S_StartSound(NULL, sfx_wpnup);
		}
		return(false);
	}
	gaveAmmo = P_GiveAmmo(player, wpnlev1info[weapon].ammo,
		GetWeaponAmmo[weapon]);
	if(player->weaponowned[weapon])
	{
		gaveWeapon = false;
	}
	else
	{
		gaveWeapon = true;
		player->weaponowned[weapon] = true;
		if(WeaponValue[weapon] > WeaponValue[player->readyweapon])
		{ // Only switch to more powerful weapons
			player->pendingweapon = weapon;
		}
	}
	return(gaveWeapon || gaveAmmo);
}

//---------------------------------------------------------------------------
//
// FUNC P_GiveBody
//
// Returns false if the body isn't needed at all.
//
//---------------------------------------------------------------------------

boolean P_GiveBody(player_t *player, int num)
{
	int max;

	max = MAXHEALTH;
	if(player->chickenTics)
	{
		max = MAXCHICKENHEALTH;
	}
	if(player->health >= max)
	{
		return(false);
	}
	player->health += num;
	if(player->health > max)
	{
		player->health = max;
	}
	player->mo->health = player->health;
	return(true);
}

//---------------------------------------------------------------------------
//
// FUNC P_GiveArmor
//
// Returns false if the armor is worse than the current armor.
//
//---------------------------------------------------------------------------

boolean P_GiveArmor(player_t *player, int armortype)
{
	int hits;

	hits = armortype*100;
	if(player->armorpoints >= hits)
	{
		return(false);
	}
	player->armortype = armortype;
	player->armorpoints = hits;
	return(true);
}

//---------------------------------------------------------------------------
//
// PROC P_GiveKey
//
//---------------------------------------------------------------------------

void P_GiveKey(player_t *player, keytype_t key)
{
	extern int playerkeys;
	extern vertex_t KeyPoints[];
	
	if(player->keys[key])
	{
		return;
	}
	if(player == &players[consoleplayer])
	{
		playerkeys |= 1<<key;
		KeyPoints[key].x = 0;
		KeyPoints[key].y = 0;
	}
	player->bonuscount = BONUSADD;
	player->keys[key] = true;
}

//---------------------------------------------------------------------------
//
// FUNC P_GivePower
//
// Returns true if power accepted.
//
//---------------------------------------------------------------------------

boolean P_GivePower(player_t *player, powertype_t power)
{
	if(power == pw_invulnerability)
	{
		if(player->powers[power] > BLINKTHRESHOLD)
		{ // Already have it
			return(false);
		}
		player->powers[power] = INVULNTICS;
		return(true);
	}
	if(power == pw_weaponlevel2)
	{
		if(player->powers[power] > BLINKTHRESHOLD)
		{ // Already have it
			return(false);
		}
		player->powers[power] = WPNLEV2TICS;
		return(true);
	}
	if(power == pw_invisibility)
	{
		if(player->powers[power] > BLINKTHRESHOLD)
		{ // Already have it
			return(false);
		}
		player->powers[power] = INVISTICS;
		player->mo->flags |= MF_SHADOW;
		return(true);
	}
	if(power == pw_flight)
	{
		if(player->powers[power] > BLINKTHRESHOLD)
		{ // Already have it
			return(false);
		}
		player->powers[power] = FLIGHTTICS;
		player->mo->flags2 |= MF2_FLY;
		player->mo->flags |= MF_NOGRAVITY;
		if(player->mo->z <= player->mo->floorz)
		{
			player->flyheight = 10; // thrust the player in the air a bit
		}
		return(true);
	}
	if(power == pw_infrared)
	{
		if(player->powers[power] > BLINKTHRESHOLD)
		{ // Already have it
			return(false);
		}
		player->powers[power] = INFRATICS;
		return(true);
	}
/*
	if(power == pw_ironfeet)
	{
		player->powers[power] = IRONTICS;
		return(true);
	}
	if(power == pw_strength)
	{
		P_GiveBody(player, 100);
		player->powers[power] = 1;
		return(true);
	}
*/
	if(player->powers[power])
	{
		return(false); // already got it
	}
	player->powers[power] = 1;
	return(true);
}

//---------------------------------------------------------------------------
//
// FUNC P_GiveArtifact
//
// Returns true if artifact accepted.
//
//---------------------------------------------------------------------------

boolean P_GiveArtifact(player_t *player, artitype_t arti, mobj_t *mo)
{
	int i;

	i = 0;
	while(player->inventory[i].type != arti && i < player->inventorySlotNum)
	{
		i++;
	}
	if(i == player->inventorySlotNum)
	{
		player->inventory[i].count = 1;
		player->inventory[i].type = arti;
		player->inventorySlotNum++;
	}
	else
	{
		if(player->inventory[i].count >= 16)
		{ // Player already has 16 of this item
			return(false);
		}
		player->inventory[i].count++;
	}
	if(player->artifactCount == 0)
	{
		player->readyArtifact = arti;
	}
	player->artifactCount++;
	if(mo && (mo->flags&MF_COUNTITEM))
	{
		player->itemcount++;
	}
	return(true);
}

//---------------------------------------------------------------------------
//
// PROC P_SetDormantArtifact
//
// Removes the MF_SPECIAL flag, and initiates the artifact pickup
// animation.
//
//---------------------------------------------------------------------------

void P_SetDormantArtifact(mobj_t *arti)
{
	arti->flags &= ~MF_SPECIAL;
	if(deathmatch && (arti->type != MT_ARTIINVULNERABILITY)
		&& (arti->type != MT_ARTIINVISIBILITY))
	{
		P_SetMobjState(arti, S_DORMANTARTI1);
	}
	else
	{ // Don't respawn
		P_SetMobjState(arti, S_DEADARTI1);
	}
	S_StartSound(arti, sfx_artiup);
}

//---------------------------------------------------------------------------
//
// PROC A_RestoreArtifact
//
//---------------------------------------------------------------------------

void A_RestoreArtifact(mobj_t *arti)
{
	arti->flags |= MF_SPECIAL;
	P_SetMobjState(arti, arti->info->spawnstate);
	S_StartSound(arti, sfx_respawn);
}

//----------------------------------------------------------------------------
//
// PROC P_HideSpecialThing
//
//----------------------------------------------------------------------------

void P_HideSpecialThing(mobj_t *thing)
{
	thing->flags &= ~MF_SPECIAL;
	thing->flags2 |= MF2_DONTDRAW;
	P_SetMobjState(thing, S_HIDESPECIAL1);
}

//---------------------------------------------------------------------------
//
// PROC A_RestoreSpecialThing1
//
// Make a special thing visible again.
//
//---------------------------------------------------------------------------

void A_RestoreSpecialThing1(mobj_t *thing)
{
	if(thing->type == MT_WMACE)
	{ // Do random mace placement
		P_RepositionMace(thing);
	}
	thing->flags2 &= ~MF2_DONTDRAW;
	S_StartSound(thing, sfx_respawn);
}

//---------------------------------------------------------------------------
//
// PROC A_RestoreSpecialThing2
//
//---------------------------------------------------------------------------

void A_RestoreSpecialThing2(mobj_t *thing)
{
	thing->flags |= MF_SPECIAL;
	P_SetMobjState(thing, thing->info->spawnstate);
}

//---------------------------------------------------------------------------
//
// PROC P_TouchSpecialThing
//
//---------------------------------------------------------------------------

void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher)
{
	int i;
	player_t *player;
	fixed_t delta;
	int sound;
	boolean respawn;

	delta = special->z-toucher->z;
	if(delta > toucher->height || delta < -32*FRACUNIT)
	{ // Out of reach
		return;
	}
	if(toucher->health <= 0)
	{ // Toucher is dead
		return;
	}
	sound = sfx_itemup;
	player = toucher->player;
	respawn = true;
	switch(special->sprite)
	{
		// Items
		case SPR_PTN1: // Item_HealingPotion
			if(!P_GiveBody(player, 10))
			{
				return;
			}
			P_SetMessage(player, TXT_ITEMHEALTH, false);
			break;
		case SPR_SHLD: // Item_Shield1
			if(!P_GiveArmor(player, 1))
			{
				return;
			}
			P_SetMessage(player, TXT_ITEMSHIELD1, false);
			break;
		case SPR_SHD2: // Item_Shield2
			if(!P_GiveArmor(player, 2))
			{
				return;
			}
			P_SetMessage(player, TXT_ITEMSHIELD2, false);
			break;
		case SPR_BAGH: // Item_BagOfHolding
			if(!player->backpack)
			{
				for(i = 0; i < NUMAMMO; i++)
				{
					player->maxammo[i] *= 2;
				}
				player->backpack = true;
			}
			P_GiveAmmo(player, am_goldwand, AMMO_GWND_WIMPY);
			P_GiveAmmo(player, am_blaster, AMMO_BLSR_WIMPY);
			P_GiveAmmo(player, am_crossbow, AMMO_CBOW_WIMPY);
			P_GiveAmmo(player, am_skullrod, AMMO_SKRD_WIMPY);
			P_GiveAmmo(player, am_phoenixrod, AMMO_PHRD_WIMPY);
			P_SetMessage(player, TXT_ITEMBAGOFHOLDING, false);
			break;
		case SPR_SPMP: // Item_SuperMap
			if(!P_GivePower(player, pw_allmap))
			{
				return;
			}
			P_SetMessage(player, TXT_ITEMSUPERMAP, false);
			break;

		// Keys
		case SPR_BKYY: // Key_Blue
			if(!player->keys[key_blue])
			{
				P_SetMessage(player, TXT_GOTBLUEKEY, false);
			}
			P_GiveKey(player, key_blue);
			sound = sfx_keyup;
			if(!netgame)
			{
				break;
			}
			return;
		case SPR_CKYY: // Key_Yellow
			if(!player->keys[key_yellow])
			{
				P_SetMessage(player, TXT_GOTYELLOWKEY, false);
			}
			sound = sfx_keyup;
			P_GiveKey(player, key_yellow);
			if(!netgame)
			{
				break;
			}
			return;
		case SPR_AKYY: // Key_Green
			if(!player->keys[key_green])
			{
				P_SetMessage(player, TXT_GOTGREENKEY, false);
			}
			sound = sfx_keyup;
			P_GiveKey(player, key_green);
			if(!netgame)
			{
				break;
			}
			return;

		// Artifacts
		case SPR_PTN2: // Arti_HealingPotion
			if(P_GiveArtifact(player, arti_health, special))
			{
				P_SetMessage(player, TXT_ARTIHEALTH, false);
				P_SetDormantArtifact(special);
			}
			return;
		case SPR_SOAR: // Arti_Fly
			if(P_GiveArtifact(player, arti_fly, special))
			{
				P_SetMessage(player, TXT_ARTIFLY, false);
				P_SetDormantArtifact(special);
			}
			return;
		case SPR_INVU: // Arti_Invulnerability
			if(P_GiveArtifact(player, arti_invulnerability, special))
			{
				P_SetMessage(player, TXT_ARTIINVULNERABILITY, false);
				P_SetDormantArtifact(special);
			}
			return;
		case SPR_PWBK: // Arti_TomeOfPower
			if(P_GiveArtifact(player, arti_tomeofpower, special))
			{
				P_SetMessage(player, TXT_ARTITOMEOFPOWER, false);
				P_SetDormantArtifact(special);
			}
			return;
		case SPR_INVS: // Arti_Invisibility
			if(P_GiveArtifact(player, arti_invisibility, special))
			{
				P_SetMessage(player, TXT_ARTIINVISIBILITY, false);
				P_SetDormantArtifact(special);
			}
			return;
		case SPR_EGGC: // Arti_Egg
			if(P_GiveArtifact(player, arti_egg, special))
			{
				P_SetMessage(player, TXT_ARTIEGG, false);
				P_SetDormantArtifact(special);
			}
			return;
		case SPR_SPHL: // Arti_SuperHealth
			if(P_GiveArtifact(player, arti_superhealth, special))
			{
				P_SetMessage(player, TXT_ARTISUPERHEALTH, false);
				P_SetDormantArtifact(special);
			}
			return;
		case SPR_TRCH: // Arti_Torch
			if(P_GiveArtifact(player, arti_torch, special))
			{
				P_SetMessage(player, TXT_ARTITORCH, false);
				P_SetDormantArtifact(special);
			}
			return;
		case SPR_FBMB: // Arti_FireBomb
			if(P_GiveArtifact(player, arti_firebomb, special))
			{
				P_SetMessage(player, TXT_ARTIFIREBOMB, false);
				P_SetDormantArtifact(special);
			}
			return;
		case SPR_ATLP: // Arti_Teleport
			if(P_GiveArtifact(player, arti_teleport, special))
			{
				P_SetMessage(player, TXT_ARTITELEPORT, false);
				P_SetDormantArtifact(special);
			}
			return;

		// Ammo
		case SPR_AMG1: // Ammo_GoldWandWimpy
			if(!P_GiveAmmo(player, am_goldwand, special->health))
			{
				return;
			}
			P_SetMessage(player, TXT_AMMOGOLDWAND1, false);
			break;
		case SPR_AMG2: // Ammo_GoldWandHefty
			if(!P_GiveAmmo(player, am_goldwand, special->health))
			{
				return;
			}
			P_SetMessage(player, TXT_AMMOGOLDWAND2, false);
			break;
		case SPR_AMM1: // Ammo_MaceWimpy
			if(!P_GiveAmmo(player, am_mace, special->health))
			{
				return;
			}
			P_SetMessage(player, TXT_AMMOMACE1, false);
			break;
		case SPR_AMM2: // Ammo_MaceHefty
			if(!P_GiveAmmo(player, am_mace, special->health))
			{
				return;
			}
			P_SetMessage(player, TXT_AMMOMACE2, false);
			break;
		case SPR_AMC1: // Ammo_CrossbowWimpy
			if(!P_GiveAmmo(player, am_crossbow, special->health))
			{
				return;
			}
			P_SetMessage(player, TXT_AMMOCROSSBOW1, false);
			break;
		case SPR_AMC2: // Ammo_CrossbowHefty
			if(!P_GiveAmmo(player, am_crossbow, special->health))
			{
				return;
			}
			P_SetMessage(player, TXT_AMMOCROSSBOW2, false);
			break;
		case SPR_AMB1: // Ammo_BlasterWimpy
			if(!P_GiveAmmo(player, am_blaster, special->health))
			{
				return;
			}
			P_SetMessage(player, TXT_AMMOBLASTER1, false);
			break;
		case SPR_AMB2: // Ammo_BlasterHefty
			if(!P_GiveAmmo(player, am_blaster, special->health))
			{
				return;
			}
			P_SetMessage(player, TXT_AMMOBLASTER2, false);
			break;
		case SPR_AMS1: // Ammo_SkullRodWimpy
			if(!P_GiveAmmo(player, am_skullrod, special->health))
			{
				return;
			}
			P_SetMessage(player, TXT_AMMOSKULLROD1, false);
			break;
		case SPR_AMS2: // Ammo_SkullRodHefty
			if(!P_GiveAmmo(player, am_skullrod, special->health))
			{
				return;
			}
			P_SetMessage(player, TXT_AMMOSKULLROD2, false);
			break;
		case SPR_AMP1: // Ammo_PhoenixRodWimpy
			if(!P_GiveAmmo(player, am_phoenixrod, special->health))
			{
				return;
			}
			P_SetMessage(player, TXT_AMMOPHOENIXROD1, false);
			break;
		case SPR_AMP2: // Ammo_PhoenixRodHefty
			if(!P_GiveAmmo(player, am_phoenixrod, special->health))
			{
				return;
			}
			P_SetMessage(player, TXT_AMMOPHOENIXROD2, false);
			break;

		// Weapons
		case SPR_WMCE: // Weapon_Mace
			if(!P_GiveWeapon(player, wp_mace))
			{
				return;
			}
			P_SetMessage(player, TXT_WPNMACE, false);
			sound = sfx_wpnup;
			break;
		case SPR_WBOW: // Weapon_Crossbow
			if(!P_GiveWeapon(player, wp_crossbow))
			{
				return;
			}
			P_SetMessage(player, TXT_WPNCROSSBOW, false);
			sound = sfx_wpnup;
			break;
		case SPR_WBLS: // Weapon_Blaster
			if(!P_GiveWeapon(player, wp_blaster))
			{
				return;
			}
			P_SetMessage(player, TXT_WPNBLASTER, false);
			sound = sfx_wpnup;
			break;
		case SPR_WSKL: // Weapon_SkullRod
			if(!P_GiveWeapon(player, wp_skullrod))
			{
				return;
			}
			P_SetMessage(player, TXT_WPNSKULLROD, false);
			sound = sfx_wpnup;
			break;
		case SPR_WPHX: // Weapon_PhoenixRod
			if(!P_GiveWeapon(player, wp_phoenixrod))
			{
				return;
			}
			P_SetMessage(player, TXT_WPNPHOENIXROD, false);
			sound = sfx_wpnup;
			break;
		case SPR_WGNT: // Weapon_Gauntlets
			if(!P_GiveWeapon(player, wp_gauntlets))
			{
				return;
			}
			P_SetMessage(player, TXT_WPNGAUNTLETS, false);
			sound = sfx_wpnup;
			break;
		default:
			I_Error("P_SpecialThing: Unknown gettable thing");
	}
	if(special->flags&MF_COUNTITEM)
	{
		player->itemcount++;
	}
	if(deathmatch && respawn && !(special->flags&MF_DROPPED))
	{
		P_HideSpecialThing(special);
	}
	else
	{
		P_RemoveMobj(special);
	}
	player->bonuscount += BONUSADD;
	if(player == &players[consoleplayer])
	{
		S_StartSound(NULL, sound);
		SB_PaletteFlash();
	}
}

//---------------------------------------------------------------------------
//
// PROC P_KillMobj
//
//---------------------------------------------------------------------------

void P_KillMobj(mobj_t *source, mobj_t *target)
{
	target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY|MF_NOGRAVITY);
	target->flags |= MF_CORPSE|MF_DROPOFF;
	target->flags2 &= ~MF2_PASSMOBJ;
	target->height >>= 2;
	if(source && source->player)
	{
		if(target->flags&MF_COUNTKILL)
		{ // Count for intermission
			source->player->killcount++;
		}
		if(target->player)
		{ // Frag stuff
			if(target == source)
			{ // Self-frag
				target->player->frags[target->player-players]--;
			}
			else
			{
				source->player->frags[target->player-players]++;
				if(source->player == &players[consoleplayer])
				{
					S_StartSound(NULL, sfx_gfrag);
				}
				if(source->player->chickenTics)
				{ // Make a super chicken
					P_GivePower(source->player, pw_weaponlevel2);
				}
			}
		}
	}
	else if(!netgame && (target->flags&MF_COUNTKILL))
	{ // Count all monster deaths
		players[0].killcount++;
	}
	if(target->player)
	{
		if(!source)
		{ // Self-frag
			target->player->frags[target->player-players]--;
		}
		target->flags &= ~MF_SOLID;
		target->flags2 &= ~MF2_FLY;
		target->player->powers[pw_flight] = 0;
		target->player->powers[pw_weaponlevel2] = 0;
		target->player->playerstate = PST_DEAD;
		P_DropWeapon(target->player);
		if(target->flags2&MF2_FIREDAMAGE)
		{ // Player flame death
			P_SetMobjState(target, S_PLAY_FDTH1);
			//S_StartSound(target, sfx_hedat1); // Burn sound
			return;
		}
	}
	if(target->health < -(target->info->spawnhealth>>1)
		&& target->info->xdeathstate)
	{ // Extreme death
		P_SetMobjState(target, target->info->xdeathstate);
	}
	else
	{ // Normal death
		P_SetMobjState(target, target->info->deathstate);
	}
	target->tics -= P_Random()&3;
//	I_StartSound(&actor->r, actor->info->deathsound);
}

//---------------------------------------------------------------------------
//
// FUNC P_MinotaurSlam
//
//---------------------------------------------------------------------------

void P_MinotaurSlam(mobj_t *source, mobj_t *target)
{
	angle_t angle;
	fixed_t thrust;

	angle = R_PointToAngle2(source->x, source->y, target->x, target->y);
	angle >>= ANGLETOFINESHIFT;
	thrust = 16*FRACUNIT+(P_Random()<<10);
	target->momx += FixedMul(thrust, finecosine[angle]);
	target->momy += FixedMul(thrust, finesine[angle]);
	P_DamageMobj(target, NULL, NULL, HITDICE(6));
	if(target->player)
	{
		target->reactiontime = 14+(P_Random()&7);
	}
}

//---------------------------------------------------------------------------
//
// FUNC P_TouchWhirlwind
//
//---------------------------------------------------------------------------

void P_TouchWhirlwind(mobj_t *target)
{
	int randVal;

	target->angle += (P_Random()-P_Random())<<20;
	target->momx += (P_Random()-P_Random())<<10;
	target->momy += (P_Random()-P_Random())<<10;
	if(leveltime&16 && !(target->flags2&MF2_BOSS))
	{
		randVal = P_Random();
		if(randVal > 160)
		{
			randVal = 160;
		}
		target->momz += randVal<<10;
		if(target->momz > 12*FRACUNIT)
		{
			target->momz = 12*FRACUNIT;
		}
	}
	if(!(leveltime&7))
	{
		P_DamageMobj(target, NULL, NULL, 3);
	}
}

//---------------------------------------------------------------------------
//
// FUNC P_ChickenMorphPlayer
//
// Returns true if the player gets turned into a chicken.
//
//---------------------------------------------------------------------------

boolean P_ChickenMorphPlayer(player_t *player)
{
	mobj_t *pmo;
	mobj_t *fog;
	mobj_t *chicken;
	fixed_t x;
	fixed_t y;
	fixed_t z;
	angle_t angle;
	int oldFlags2;

	if(player->chickenTics)
	{
		if((player->chickenTics < CHICKENTICS-TICSPERSEC)
			&& !player->powers[pw_weaponlevel2])
		{ // Make a super chicken
			P_GivePower(player, pw_weaponlevel2);
		}
		return(false);
	}
	if(player->powers[pw_invulnerability])
	{ // Immune when invulnerable
		return(false);
	}
	pmo = player->mo;
	x = pmo->x;
	y = pmo->y;
	z = pmo->z;
	angle = pmo->angle;
	oldFlags2 = pmo->flags2;
	P_SetMobjState(pmo, S_FREETARGMOBJ);
	fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG);
	S_StartSound(fog, sfx_telept);
	chicken = P_SpawnMobj(x, y, z, MT_CHICPLAYER);
	chicken->special1 = player->readyweapon;
	chicken->angle = angle;
	chicken->player = player;
	player->health = chicken->health = MAXCHICKENHEALTH;
	player->mo = chicken;
	player->armorpoints = player->armortype = 0;
	player->powers[pw_invisibility] = 0;
	player->powers[pw_weaponlevel2] = 0;
	if(oldFlags2&MF2_FLY)
	{
		chicken->flags2 |= MF2_FLY;
	}
	player->chickenTics = CHICKENTICS;
	P_ActivateBeak(player);
	return(true);
}

//---------------------------------------------------------------------------
//
// FUNC P_ChickenMorph
//
//---------------------------------------------------------------------------

boolean P_ChickenMorph(mobj_t *actor)
{
	mobj_t *fog;
	mobj_t *chicken;
	mobj_t *target;
	mobjtype_t moType;
	fixed_t x;
	fixed_t y;
	fixed_t z;
	angle_t angle;
	int ghost;

	if(actor->player)
	{
		return(false);
	}
	moType = actor->type;
	switch(moType)
	{
		case MT_POD:
		case MT_CHICKEN:
		case MT_HEAD:
		case MT_MINOTAUR:
		case MT_SORCERER1:
		case MT_SORCERER2:
			return(false);
		default:
			break;
	}
	x = actor->x;
	y = actor->y;
	z = actor->z;
	angle = actor->angle;
	ghost = actor->flags&MF_SHADOW;
	target = actor->target;
	P_SetMobjState(actor, S_FREETARGMOBJ);
	fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG);
	S_StartSound(fog, sfx_telept);
	chicken = P_SpawnMobj(x, y, z, MT_CHICKEN);
	chicken->special2 = moType;
	chicken->special1 = CHICKENTICS+P_Random();
	chicken->flags |= ghost;
	chicken->target = target;
	chicken->angle = angle;
	return(true);
}

//---------------------------------------------------------------------------
//
// FUNC P_AutoUseChaosDevice
//
//---------------------------------------------------------------------------

boolean P_AutoUseChaosDevice(player_t *player)
{
	int i;

	for(i = 0; i < player->inventorySlotNum; i++)
	{
		if(player->inventory[i].type == arti_teleport)
		{
			P_PlayerUseArtifact(player, arti_teleport);
			player->health = player->mo->health = (player->health+1)/2;
			return(true);
		}
	}
	return(false);
}

//---------------------------------------------------------------------------
//
// PROC P_AutoUseHealth
//
//---------------------------------------------------------------------------

void P_AutoUseHealth(player_t *player, int saveHealth)
{
	int i;
	int count;
	int normalCount;
	int normalSlot;
	int superCount;
	int superSlot;

	normalSlot = superSlot = 0;
	normalCount = superCount = 0;
	for(i = 0; i < player->inventorySlotNum; i++)
	{
		if(player->inventory[i].type == arti_health)
		{
			normalSlot = i;
			normalCount = player->inventory[i].count;
		}
		else if(player->inventory[i].type == arti_superhealth)
		{
			superSlot = i;
			superCount = player->inventory[i].count;
		}
	}
	if((gameskill == sk_baby) && (normalCount*25 >= saveHealth))
	{ // Use quartz flasks
		count = (saveHealth+24)/25;
		for(i = 0; i < count; i++)
		{
			player->health += 25;
			P_PlayerRemoveArtifact(player, normalSlot);
		}
	}
	else if(superCount*100 >= saveHealth)
	{ // Use mystic urns
		count = (saveHealth+99)/100;
		for(i = 0; i < count; i++)
		{
			player->health += 100;
			P_PlayerRemoveArtifact(player, superSlot);
		}
	}
	else if((gameskill == sk_baby)
		&& (superCount*100+normalCount*25 >= saveHealth))
	{ // Use mystic urns and quartz flasks
		count = (saveHealth+24)/25;
		saveHealth -= count*25;
		for(i = 0; i < count; i++)
		{
			player->health += 25;
			P_PlayerRemoveArtifact(player, normalSlot);
		}
		count = (saveHealth+99)/100;
		for(i = 0; i < count; i++)
		{
			player->health += 100;
			P_PlayerRemoveArtifact(player, normalSlot);
		}
	}
	player->mo->health = player->health;
}

/*
=================
=
= P_DamageMobj
=
= Damages both enemies and players
= inflictor is the thing that caused the damage
= 		creature or missile, can be NULL (slime, etc)
= source is the thing to target after taking damage
=		creature or NULL
= Source and inflictor are the same for melee attacks
= source can be null for barrel explosions and other environmental stuff
==================
*/

void P_DamageMobj
(
	mobj_t *target,
	mobj_t *inflictor,
	mobj_t *source,
	int	damage
)
{
	unsigned ang;
	int saved;
	player_t *player;
	fixed_t thrust;
	int temp;

	if(!(target->flags&MF_SHOOTABLE))
	{
		// Shouldn't happen
		return;
	}
	if(target->health <= 0)
	{
		return;
	}
	if(target->flags&MF_SKULLFLY)
	{
		if(target->type == MT_MINOTAUR)
		{ // Minotaur is invulnerable during charge attack
			return;
		}
		target->momx = target->momy = target->momz = 0;
	}
	player = target->player;
	if(player && gameskill == sk_baby)
	{
		// Take half damage in trainer mode
		damage >>= 1;
	}
	// Special damage types
	if(inflictor)
	{
		switch(inflictor->type)
		{
			case MT_EGGFX:
				if(player)
				{
					P_ChickenMorphPlayer(player);
				}
				else
				{
					P_ChickenMorph(target);
				}
				return; // Always return
			case MT_WHIRLWIND:
				P_TouchWhirlwind(target);
				return;
			case MT_MINOTAUR:
				if(inflictor->flags&MF_SKULLFLY)
				{ // Slam only when in charge mode
					P_MinotaurSlam(inflictor, target);
					return;
				}
				break;
			case MT_MACEFX4: // Death ball
				if((target->flags2&MF2_BOSS) || target->type == MT_HEAD)
				{ // Don't allow cheap boss kills
					break;
				}
				else if(target->player)
				{ // Player specific checks
					if(target->player->powers[pw_invulnerability])
					{ // Can't hurt invulnerable players
						break;
					}
					if(P_AutoUseChaosDevice(target->player))
					{ // Player was saved using chaos device
						return;
					}
				}
				damage = 10000; // Something's gonna die
				break;
			case MT_PHOENIXFX2: // Flame thrower
				if(target->player && P_Random() < 128)
				{ // Freeze player for a bit
					target->reactiontime += 4;
				}
				break;
			case MT_RAINPLR1: // Rain missiles
			case MT_RAINPLR2:
			case MT_RAINPLR3:
			case MT_RAINPLR4:
				if(target->flags2&MF2_BOSS)
				{ // Decrease damage for bosses
					damage = (P_Random()&7)+1;
				}
				break;
			case MT_HORNRODFX2:
			case MT_PHOENIXFX1:
				if(target->type == MT_SORCERER2 && P_Random() < 96)
				{ // D'Sparil teleports away
					P_DSparilTeleport(target);
					return;
				}
				break;
			case MT_BLASTERFX1:
			case MT_RIPPER:
				if(target->type == MT_HEAD)
				{ // Less damage to Ironlich bosses
					damage = P_Random()&1;
					if(!damage)
					{
						return;
					}
				}
				break;
			default:
				break;
		}
	}
	// Push the target unless source is using the gauntlets
	if(inflictor && (!source || !source->player
		|| source->player->readyweapon != wp_gauntlets)
		&& !(inflictor->flags2&MF2_NODMGTHRUST))
	{
		ang = R_PointToAngle2(inflictor->x, inflictor->y,
			target->x, target->y);
		//thrust = damage*(FRACUNIT>>3)*100/target->info->mass;
		thrust = damage*(FRACUNIT>>3)*150/target->info->mass;
		// make fall forwards sometimes
		if((damage < 40) && (damage > target->health)
			&& (target->z-inflictor->z > 64*FRACUNIT) && (P_Random()&1))
		{
			ang += ANG180;
			thrust *= 4;
		}
		ang >>= ANGLETOFINESHIFT;
		if(source && source->player && (source == inflictor)
			&& source->player->powers[pw_weaponlevel2]
			&& source->player->readyweapon == wp_staff)
		{
			// Staff power level 2
			target->momx += FixedMul(10*FRACUNIT, finecosine[ang]);
			target->momy += FixedMul(10*FRACUNIT, finesine[ang]);
			if(!(target->flags&MF_NOGRAVITY))
			{
				target->momz += 5*FRACUNIT;
			}
		}
		else
		{
			target->momx += FixedMul(thrust, finecosine[ang]);
			target->momy += FixedMul(thrust, finesine[ang]);
		}
	}

	//
	// player specific
	//
	if(player)
	{
		// end of game hell hack
		//if(target->subsector->sector->special == 11
		//	&& damage >= target->health)
		//{
		//	damage = target->health - 1;
		//}

		if(damage < 1000 && ((player->cheats&CF_GODMODE)
			|| player->powers[pw_invulnerability]))
		{
			return;
		}
		if(player->armortype)
		{
			if(player->armortype == 1)
			{
				saved = damage>>1;
			}
			else
			{
				saved = (damage>>1)+(damage>>2);
			}
			if(player->armorpoints <= saved)
			{
				// armor is used up
				saved = player->armorpoints;
				player->armortype = 0;
			}
			player->armorpoints -= saved;
			damage -= saved;
		}
		if(damage >= player->health
			&& ((gameskill == sk_baby) || deathmatch)
			&& !player->chickenTics)
		{ // Try to use some inventory health
			P_AutoUseHealth(player, damage-player->health+1);
		}
		player->health -= damage; // mirror mobj health here for Dave
		if(player->health < 0)
		{
			player->health = 0;
		}
		player->attacker = source;
		player->damagecount += damage; // add damage after armor / invuln
		if(player->damagecount > 100)
		{
			player->damagecount = 100; // teleport stomp does 10k points...
		}
		temp = damage < 100 ? damage : 100;
		if(player == &players[consoleplayer])
		{
			//I_Tactile(40, 10, 40+temp*2);
			SB_PaletteFlash();
		}
	}

	//
	// do the damage
	//
	target->health -= damage;
	if(target->health <= 0)
	{ // Death
		target->special1 = damage;
		if(target->type == MT_POD && source && source->type != MT_POD)
		{ // Make sure players get frags for chain-reaction kills
			target->target = source;
		}
		if(player && inflictor && !player->chickenTics)
		{ // Check for flame death
			if((inflictor->flags2&MF2_FIREDAMAGE)
				|| ((inflictor->type == MT_PHOENIXFX1)
				&& (target->health > -50) && (damage > 25)))
			{
				target->flags2 |= MF2_FIREDAMAGE;
			}
		}
		P_KillMobj(source, target);
		return;
	}
	if((P_Random() < target->info->painchance)
		&& !(target->flags&MF_SKULLFLY))
	{
		target->flags |= MF_JUSTHIT; // fight back!
		P_SetMobjState(target, target->info->painstate);
	}
	target->reactiontime = 0; // we're awake now...
	if(!target->threshold && source && !(source->flags2&MF2_BOSS)
		&& !(target->type == MT_SORCERER2 && source->type == MT_WIZARD))
	{
		// Target actor is not intent on another actor,
		// so make him chase after source
		target->target = source;
		target->threshold = BASETHRESHOLD;
		if(target->state == &states[target->info->spawnstate]
			&& target->info->seestate != S_NULL)
		{
			P_SetMobjState(target, target->info->seestate);
		}
	}
}
